﻿using Microsoft.Extensions.Logging;
using MyTRCP.Common.Models;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using System.Net;
using System.Net.Sockets;
using Tmds.DBus;

namespace MyTRCP.Common.MyProtocol
{
    public class ClientSide : IDisposable
    {
        private readonly IPEndPoint _remoteEndPoint;
        private readonly Socket _clientSock;
        private readonly BlockingQueue<byte[]> _devCtrlTxQueue;
        private readonly BlockingQueue<byte[]> _devAudioTxQueue;
        private readonly BlockingQueue<byte[]> _devCtrlRxQueue;
        private readonly BlockingQueue<byte[]> _devAudioRxQueue;
        private readonly CancellationTokenSource _cts;
        private readonly AsymmetricCipherKeyPair _ecClientIdAuthKeyPair;
        private SessionInfo? _sessionInfo;
        private AuthStageInfo? _authStageInfo;
        private readonly List<ECPublicKeyParameters> _trustedBridgePublicKeys;
        private readonly List<LcpReqInfo> _lcpReqInfos;
        private bool _recvRunning;
        private bool _sendCtrlRunning;
        private bool _sendAudioRunning;
        private bool _takeQueueToSendRunning;
        public event EventHandler<RemoteDevLinkStatusChangedEventArgs>? RemoteDevLinkStatusChanged;
        public event EventHandler? SessionDroped;
        private readonly ILogger<ClientSide> _logger;
        private readonly BlockingQueue<MyWaitToSendDataInfo> _waitToSendQueue;
        private int _eachPayloadRepeatTimes = 3;
        private readonly byte[] _buf;
        private readonly ManualResetEvent _exitEvent;
        public bool RunningStatus
        {
            get
            {
                return _recvRunning || _sendAudioRunning || _sendCtrlRunning || _takeQueueToSendRunning;
            }
        }

        public int EachPayloadRepeatTimes { get => _eachPayloadRepeatTimes; set => _eachPayloadRepeatTimes = value; }

        ~ClientSide()
        {
            Dispose();
        }
        public ClientSide(ILogger<ClientSide> logger, IPEndPoint remoteEndPoint, AsymmetricCipherKeyPair ecClientIdAuthKeyPair, List<ECPublicKeyParameters> trustedBridgePublicKeys, BlockingQueue<byte[]> devCtrlRxQ, BlockingQueue<byte[]> devAudioRxQ, BlockingQueue<byte[]> devCtrlTxQ, BlockingQueue<byte[]> devAudioTxQ, int repeatTimes = 3)
        {
            _eachPayloadRepeatTimes = repeatTimes;
            _exitEvent = new ManualResetEvent(false);
            _logger = logger;
            _buf = new byte[2048];
            _clientSock = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
            var activeUdpListeners = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
            Random r = new Random();
            int port = r.Next(60000, 65000);
            while (activeUdpListeners.Any(c => c.Port == port))
            {
                port = r.Next(60000, 65000);
            }
            _clientSock.Bind(new IPEndPoint(remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, port));
            _remoteEndPoint = remoteEndPoint;
            _devCtrlTxQueue = devCtrlTxQ;
            _devAudioTxQueue = devAudioTxQ;
            _devCtrlRxQueue = devCtrlRxQ;
            _devAudioRxQueue = devAudioRxQ;
            _cts = new CancellationTokenSource();
            _ecClientIdAuthKeyPair = ecClientIdAuthKeyPair;
            _trustedBridgePublicKeys = trustedBridgePublicKeys;
            _lcpReqInfos = new List<LcpReqInfo>();
            _waitToSendQueue = new BlockingQueue<MyWaitToSendDataInfo>();
        }
        public void Start()
        {
            if (_cts.TryReset())
                _logger.LogDebug("cts resetted.");
            Task.Run(ReceiveDataAsync);
            Task.Run(SendDevCtrlDataLoop);
            Task.Run(SendDevAudioDataLoop);
            Task.Run(TakeQueueToSendAsync);
        }
        public void Stop()
        {
            _devCtrlTxQueue.CancelTake();
            _devAudioTxQueue.CancelTake();
            _waitToSendQueue.CancelTake();
            _cts.Cancel();
        }
        public void Exit()
        {
            _exitEvent.Set();
            Stop();
        }
        public void SetExitEvent()
        {
            _exitEvent.Set();
        }
        public async Task Auth()
        {
            if (_sessionInfo == null || (_sessionInfo != null && _sessionInfo.SessionId == Guid.Empty))
            {
                try
                {
                    await Task.Run(ClientHello);
                }
                catch (Exception ex)
                {
                    throw new Exception($"认证时发生异常:{ex.Message}", ex);
                }
            }
            else
            {
                throw new Exception("已建立会话，无需再次验证。建议先关闭会话再验证");
            }
        }
        private IPEndPoint BridgeIPEndPoint
        {
            get
            {
                if (_sessionInfo == null)
                    return _remoteEndPoint;
                else if (_sessionInfo.LastEP == null)
                    return _remoteEndPoint;
                else
                    return _sessionInfo.LastEP;
            }
        }
        public void SetClientRepeatTimes(byte times)
        {
            if (_sessionInfo != null)
            {
                _sessionInfo.RepeatTimes = times;
            }
            _eachPayloadRepeatTimes = times;
        }

        public async Task<IReadOnlyCollection<BtDevInfo>?> DiscoverMyDevicesTop5()
        {
            if (_sessionInfo == null)
            {
                throw new Exception("未建立会话，请先建立会话");
            }
            return await Task.Run(async () =>
            {
                try
                {
                    LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.DISCOVER_MY_DEVICES, [], _sessionInfo, BridgeIPEndPoint);
                    if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                    {
                        MyDiscoveredDevices mdd = new MyDiscoveredDevices(lcrp.Data);
                        if (mdd.IsSuccess)
                        {
                            return mdd.BtDevs.AsReadOnly();
                        }
                        else
                        {
                            return null;
                        }
                    }
                    else
                    {
                        return null;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                    return null;
                }
            });
        }
        public async Task<bool> SetBridgeRepeatTimes(byte times)
        {
            if (_sessionInfo == null)
            {
                throw new Exception("未建立会话，请先建立会话");
            }
            return await Task.Run(async () =>
            {
                try
                {
                    LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.SET_REPEAT_TIMES, [times], _sessionInfo, BridgeIPEndPoint);
                    if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                    return false;
                }
            });
        }
        public async Task<bool> SessionGoodbye()
        {
            if (_sessionInfo == null)
            {
                throw new Exception("没有建立会话无需关闭会话");
            }
            return await Task.Run(async () =>
               {
                   try
                   {
                       LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.SESSION_GOODBYE, [], _sessionInfo, BridgeIPEndPoint);
                       if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                       {
                           _sessionInfo = null;
                           _logger.LogDebug("会话goodbye成功");
                           return true;
                       }
                       else
                       {
                           _logger.LogDebug($"会话goodbye失败，错误码{lcrp.StatusCode}");
                           return false;
                       }
                   }
                   catch (Exception ex)
                   {
                       _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                       return false;
                   }
               });
        }
        public async Task<bool> ConnectRemoteCtrlChannel(byte[]? macBytes = null)
        {
            if (_sessionInfo == null)
            {
                throw new Exception("未建立会话，请先建立会话");
            }
            return await Task.Run(async () =>
               {
                   try
                   {
                       LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.CONNECT_CONTROL_CHANNEL, macBytes ?? [], _sessionInfo, BridgeIPEndPoint);
                       if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                       {
                           return true;
                       }
                       else
                       {
                           return false;
                       }
                   }
                   catch (Exception ex)
                   {
                       _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                       return false;
                   }
               });
        }
        public async Task<bool> DisconnectRemoteCtrlChannel()
        {
            if (_sessionInfo == null)
            {
                throw new Exception("未建立会话，请先建立会话");
            }
            return await Task.Run(async () =>
            {
                try
                {
                    LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.DISCONNECT_CONTROL_CHANNEL, [], _sessionInfo, BridgeIPEndPoint);
                    if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                    return false;
                }
            });
        }
        public async Task<bool> ConnectRemoteAudioChannel(byte[]? macBytes = null)
        {
            if (_sessionInfo == null)
            {
                throw new Exception("未建立会话，请先建立会话");
            }
            return await Task.Run(async () =>
            {
                try
                {
                    LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.CONNECT_AUDIO_CHANNEL, macBytes ?? [], _sessionInfo, BridgeIPEndPoint);
                    if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                    return false;
                }
            });
        }
        public async Task<bool> DisconnectRemoteAudioChannel()
        {
            if (_sessionInfo == null)
            {
                throw new Exception("未建立会话，请先建立会话");
            }
            return await Task.Run(async () =>
            {
                try
                {
                    LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.DISCONNECT_AUDIO_CHANNEL, [], _sessionInfo, BridgeIPEndPoint);
                    if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                    return false;
                }
            });
        }
        public async Task<DevLinkStatusPayload> GetRemoteDevLinkStatus()
        {
            if (_sessionInfo == null)
            {
                throw new Exception("未建立会话，请先建立会话");
            }
            return await Task.Run(async () =>
            {
                try
                {
                    LinkControlResponsePayload lcrp = await SendLCPAndWaitResponse(LinkControlCmdEnum.GET_REMOTE_DEV_LINK_STATUS, [], _sessionInfo, BridgeIPEndPoint);
                    if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                    {
                        DevLinkStatusPayload dlsp = new DevLinkStatusPayload(lcrp.Data);
                        return dlsp;
                    }
                    else
                    {
                        throw new Exception($"桥接器返回错误码：{lcrp.StatusCode}");
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"发送数据异常:{ex.Message}");
                    throw new Exception("发送数据异常", ex);
                }
            });
        }
        private async Task<LinkControlResponsePayload> SendLCPAndWaitResponse(LinkControlCmdEnum cmd, byte[] data, SessionInfo sessionInfo, IPEndPoint ep)
        {
            AutoResetEvent autoResetEvent = new AutoResetEvent(false);
            LcpReqInfo info = new LcpReqInfo(sessionInfo.SessionId, cmd, autoResetEvent);
            EnqueueLinkControlPayload(data, cmd, sessionInfo.SessionId, ep, info);
            return await Task.Run(() =>
              {
                  int res = WaitHandle.WaitAny([autoResetEvent, _exitEvent], 15000);
                  switch (res)
                  {
                      case 0:
                          if (info.RespPayload != null)
                          {
                              if (info.RespPayload.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                              {
                                  return info.RespPayload;
                              }
                              throw new Exception($"命令执行失败，{info.RespPayload.StatusCode}");
                          }
                          throw new Exception("未返回数据");
                      case 1:
                          throw new OperationCanceledException("操作取消");
                      default:
                          throw new TimeoutException("等待超时");
                  }
              });
        }
        private void ClientHello()
        {
            AsymmetricCipherKeyPair keyPair = MyProtocolCryptoHelper.GenerateMyECDHKeyPair();
            _authStageInfo = new AuthStageInfo()
            {
                AuthStage = MyProtocolAuthStageEnum.CLIENT_SAY_HELLO,
                LocalKeyAgreementPrivateKey = (ECPrivateKeyParameters)keyPair.Private,
                LocalKeyAgreementPublicKey = (ECPublicKeyParameters)keyPair.Public,
                AuthCompleteEvent = new AutoResetEvent(false),
            };
            byte[] idPubKeyX = MyProtocolCryptoHelper.ECPublicKeyXToBytes(_ecClientIdAuthKeyPair.Public);
            byte[] idPubKeyY = MyProtocolCryptoHelper.ECPublicKeyYToBytes(_ecClientIdAuthKeyPair.Public);
            byte[] kaPubKeyX = MyProtocolCryptoHelper.ECPublicKeyXToBytes(keyPair.Public);
            byte[] kaPubKeyY = MyProtocolCryptoHelper.ECPublicKeyYToBytes(keyPair.Public);
            ClientHelloPayload chp = new ClientHelloPayload()
            {
                IdAuthCryptoType = IdAuthCryptoTypeEnum.SM2,
                HashAlgorithmType = HashAlgorithmTypeEnum.SM3,
                KeyAgreementAlgorithmType = KeyAgreementAlgorithmTypeEnum.ECDH,
                ClientIdAuthPublicKeyX = idPubKeyX,
                ClientIdAuthPublicKeyXLength = (ushort)idPubKeyX.Length,
                ClientIdAuthPublicKeyY = idPubKeyY,
                ClientIdAuthPublicKeyYLength = (ushort)idPubKeyY.Length,
                ClientKeyAgreementPublicKeyX = kaPubKeyX,
                ClientKeyAgreementPublicKeyXLength = (ushort)kaPubKeyX.Length,
                ClientKeyAgreementPublicKeyY = kaPubKeyY,
                ClientKeyAgreementPublicKeyYLength = (ushort)kaPubKeyY.Length,
            };
            EnqueueLinkControlPayload(chp.WriteToBytes(), LinkControlCmdEnum.HELLO, Guid.Empty, _remoteEndPoint);
            int res = WaitHandle.WaitAny([_authStageInfo.AuthCompleteEvent, _exitEvent], 5000);
            string errStr;
            switch (res)
            {
                case 0:
                    if (_authStageInfo.AuthStage == MyProtocolAuthStageEnum.AUTH_COMPLETE)
                    {
                        _logger.LogInformation("身份验证成功");
                    }
                    else
                    {
                        errStr = $"验证异常:{_authStageInfo.AuthResultCode}";
                        _logger.LogWarning(errStr);
                        throw new Exception(errStr);
                    }
                    break;
                case 1:
                    errStr = "收到退出信号，操作取消";
                    _logger.LogInformation(errStr);
                    throw new OperationCanceledException(errStr);
                default:
                    errStr = "验证超时";
                    _logger.LogWarning(errStr);
                    throw new TimeoutException(errStr);
            }
        }
        private void ProcessOnReceived(byte[] buf, int receivedLen, IPEndPoint ep)
        {
            if (receivedLen == 0)
            {
                _logger.LogDebug($"从{ep}接收到长度为0的数据，忽略");
                return;
            }
            MyProtocolPayload payload = new MyProtocolPayload(buf, 0, receivedLen);
            if (payload.ValidCheck())
            {
                if (!payload.IsClientSideSent)
                {
                    if (payload.SessionId == Guid.Empty)//auth uncomplete
                    {
                        if (payload.DataType == PayloadTypeEnum.LINK_CONTROL && payload.IsResponse)
                        {
                            LinkControlPayload lcp_r = new LinkControlPayload(payload.Data);
                            LinkControlResponsePayload lcrp = new LinkControlResponsePayload(lcp_r.Data);
                            if (_authStageInfo != null)
                            {
                                if ((!payload.IsThisDatagramChangeId && payload.DatagramId > _authStageInfo.LastRecvDatagramId) || (payload.IsThisDatagramChangeId && !_authStageInfo.LastRecvDatagramChangeId))
                                {
                                    _authStageInfo.LastRecvDatagramId = payload.DatagramId;
                                    _authStageInfo.LastRecvDatagramChangeId = payload.IsThisDatagramChangeId;
                                    _logger.LogDebug($"收到lcp回复载荷，cmd={lcp_r.Cmd},回复状态码{lcrp.StatusCode}");
                                    if (lcrp.StatusCode == LinkControlResponseStatusCodeEnum.OK)
                                    {
                                        if (_authStageInfo.AuthStage == MyProtocolAuthStageEnum.CLIENT_SAY_HELLO && lcp_r.Cmd == LinkControlCmdEnum.HELLO)
                                        {
                                            _authStageInfo.AuthStage = MyProtocolAuthStageEnum.BRIDGE_SAY_HELLO;
                                            BridgeHelloPayload bhp = new BridgeHelloPayload(lcrp.Data);
                                            ECPublicKeyParameters remoteIdPublicKey = MyProtocolCryptoHelper.ECPublicKeyFromMyBytes(bhp.BridgeIdAuthPublicKeyX, bhp.BridgeIdAuthPublicKeyY);
                                            if (MyProtocolCryptoHelper.CheckPublicKeyIsInTrustList(remoteIdPublicKey, _trustedBridgePublicKeys))
                                            {
                                                MyProtocolCryptoHelper.SecRandom.NextBytes(_authStageInfo.RemoteIdAuthChallengeRandom);
                                                _authStageInfo.RemoteIdAuthPublicKey = remoteIdPublicKey;
                                                _authStageInfo.RemoteKeyAgreementPublicKey = MyProtocolCryptoHelper.ECPublicKeyFromMyBytes(bhp.BridgeKeyAgreementPublicKeyX, bhp.BridgeKeyAgreementPublicKeyY);
                                                byte[] challengeResp = MyProtocolCryptoHelper.ChallengeResponse(bhp.ClientIdAuthChallenge, _ecClientIdAuthKeyPair.Private);
                                                byte[] challenge = MyProtocolCryptoHelper.CreateChallenge(_authStageInfo.RemoteIdAuthChallengeRandom, _authStageInfo.RemoteIdAuthPublicKey);
                                                ClientAuthPayload cap = new ClientAuthPayload() { ClientIdAuthResponse = challengeResp, ClientIdAuthResponseLength = (ushort)challengeResp.Length, BridgeIdAuthChallenge = challenge, BridgeIdAuthChallengeLength = (ushort)challenge.Length };
                                                EnqueueLinkControlPayload(cap.WriteToBytes(), LinkControlCmdEnum.CLIENT_AUTH, payload.SessionId, ep);
                                                _authStageInfo.AuthStage = MyProtocolAuthStageEnum.CLIENT_REQUEST_AUTH;
                                            }
                                            else
                                            {
                                                _authStageInfo.AuthStage = MyProtocolAuthStageEnum.AUTH_FAILED;
                                                _authStageInfo.AuthResultCode = AuthResultCodeEnum.BRIDGE_ID_NOT_TRUST;
                                                _authStageInfo.AuthCompleteEvent?.Set();
                                            }
                                        }
                                        else if (_authStageInfo.AuthStage == MyProtocolAuthStageEnum.CLIENT_REQUEST_AUTH && lcp_r.Cmd == LinkControlCmdEnum.CLIENT_AUTH)
                                        {
                                            _authStageInfo.AuthStage = MyProtocolAuthStageEnum.BRIDGE_REPLY_AUTH;
                                            BridgeAuthPayload bap = new BridgeAuthPayload(lcrp.Data);
                                            if (MyProtocolCryptoHelper.CheckChallengeResponseCorrect(bap.BridgeIdAuthResponse, _authStageInfo.RemoteIdAuthChallengeRandom) && _authStageInfo.LocalKeyAgreementPrivateKey != null && _authStageInfo.RemoteKeyAgreementPublicKey != null)
                                            {
                                                ParametersWithIV sessionKey = MyProtocolCryptoHelper.GenerateSM4KeyFromBytes(MyProtocolCryptoHelper.GetECAgreementResult(_authStageInfo.LocalKeyAgreementPrivateKey, _authStageInfo.RemoteKeyAgreementPublicKey).ToByteArray());
                                                _sessionInfo = new SessionInfo() { LastCommDateTime = DateTime.Now, LastEP = ep, LastRecvDatagramId = payload.DatagramId, LastRecvDatagramChangeId = payload.IsThisDatagramChangeId, SessionId = bap.SessionId, SessionKey = sessionKey, RepeatTimes = _eachPayloadRepeatTimes };
                                                _authStageInfo.AuthStage = MyProtocolAuthStageEnum.AUTH_COMPLETE;
                                                _authStageInfo.AuthCompleteEvent?.Set();
                                            }
                                            else
                                            {
                                                _authStageInfo.AuthStage = MyProtocolAuthStageEnum.AUTH_FAILED;
                                                _authStageInfo.AuthResultCode = AuthResultCodeEnum.BRIDGE_CHALLENGE_FAILED;
                                                _authStageInfo.AuthCompleteEvent?.Set();
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (lcp_r.Cmd == LinkControlCmdEnum.HELLO || lcp_r.Cmd == LinkControlCmdEnum.CLIENT_AUTH)
                                        {
                                            _authStageInfo.AuthStage = MyProtocolAuthStageEnum.AUTH_FAILED;
                                            switch (lcrp.StatusCode)
                                            {
                                                case LinkControlResponseStatusCodeEnum.NOT_SUPPORT:
                                                    _authStageInfo.AuthResultCode = AuthResultCodeEnum.AUTH_METHOD_NOT_SUPPORT;
                                                    break;
                                                case LinkControlResponseStatusCodeEnum.ACCESS_DENIED:
                                                    if (lcp_r.Cmd == LinkControlCmdEnum.HELLO)
                                                    {
                                                        _authStageInfo.AuthResultCode = AuthResultCodeEnum.CLIENT_ID_NOT_TRUST;
                                                    }
                                                    else
                                                    {
                                                        _authStageInfo.AuthResultCode = AuthResultCodeEnum.CLIENT_CHALLENGE_FAILED;
                                                    }
                                                    break;
                                            }
                                            _authStageInfo.AuthCompleteEvent?.Set();
                                        }
                                    }
                                    _logger.LogDebug($"client auth stage change to {_authStageInfo.AuthStage}");
                                }
                                else
                                {
                                    _logger.LogDebug($"client found that this datagram is repeated,ignored.dataId={payload.DatagramId},type={payload.DataType},checksum={payload.DataCheckSum},datalen={payload.DataLen}");
                                }
                            }
                            else
                            {
                                _logger.LogDebug("client found that the auth stage is null");
                            }
                        }
                        else
                        {
                            _logger.LogDebug("when session id is null ,not allow any other payload type but lcp");
                        }
                    }
                    else if (_sessionInfo != null && payload.SessionId == _sessionInfo.SessionId)//auth completed
                    {
                        if ((!payload.IsThisDatagramChangeId && payload.DatagramId > _sessionInfo.LastRecvDatagramId) || (payload.IsThisDatagramChangeId && !_sessionInfo.LastRecvDatagramChangeId))
                        {
                            _sessionInfo.LastRecvDatagramId = payload.DatagramId;
                            _sessionInfo.LastRecvDatagramChangeId = payload.IsThisDatagramChangeId;
                            _sessionInfo.LastEP = ep;
                            byte[] data = payload.Data;
                            if (payload.IsEncrypted)
                            {
                                if (_sessionInfo.SessionKey != null)
                                {
                                    data = MyProtocolCryptoHelper.SM4Decrypt(payload.Data, _sessionInfo.SessionKey);
                                }
                                else
                                {
                                    string errStr = "会话密钥为空，无法解密";
                                    _logger.LogError(errStr);
                                    throw new ArgumentNullException(nameof(_sessionInfo.SessionKey), errStr);
                                }
                            }
                            switch (payload.DataType)
                            {
                                case PayloadTypeEnum.LINK_CONTROL:
                                    LinkControlPayload lcp = new LinkControlPayload(data);
                                    _logger.LogDebug($"收到链路控制载荷，datagramId={payload.DatagramId},cmd={lcp.Cmd},isResponse={lcp.IsResponse}");
                                    if (lcp.IsResponse)//bridge response...
                                    {
                                        LinkControlResponsePayload lcrp = new LinkControlResponsePayload(lcp.Data);
                                        switch (lcp.Cmd)
                                        {
                                            case LinkControlCmdEnum.SESSION_GOODBYE:
                                            case LinkControlCmdEnum.CONNECT_CONTROL_CHANNEL:
                                            case LinkControlCmdEnum.CONNECT_AUDIO_CHANNEL:
                                            case LinkControlCmdEnum.DISCONNECT_CONTROL_CHANNEL:
                                            case LinkControlCmdEnum.DISCONNECT_AUDIO_CHANNEL:
                                            case LinkControlCmdEnum.GET_REMOTE_DEV_LINK_STATUS:
                                            case LinkControlCmdEnum.SET_REPEAT_TIMES:
                                            case LinkControlCmdEnum.DISCOVER_MY_DEVICES:
                                                var list = _lcpReqInfos.Where(c => c.SessionId == payload.SessionId && c.Cmd == lcp.Cmd);
                                                if (list.Count() > 0)
                                                {
                                                    var reqInfo = list.First();
                                                    reqInfo.RespPayload = lcrp;
                                                    reqInfo.RespReachedEvent.Set();
                                                    _lcpReqInfos.Remove(reqInfo);
                                                }
                                                else
                                                {
                                                    _logger.LogWarning($"this payload can't match the request,drop it.session={payload.SessionId},datagramId={payload.DatagramId},cmd={lcp.Cmd}");
                                                }
                                                break;
                                        }
                                    }
                                    else if (lcp.Cmd == LinkControlCmdEnum.REMOTE_DEV_LINK_STATUS_CHANGED_NOTICE && lcp.Data.Length > 0)
                                    {
                                        RemoteDevLinkStatusChanged?.Invoke(this, new RemoteDevLinkStatusChangedEventArgs() { NoticeType = (DevConnectStatusChangedNoticeTypeEnum)lcp.Data[0] });
                                    }
                                    break;
                                case PayloadTypeEnum.DEV_CONTROL:
                                    _devCtrlRxQueue.Add(data);
                                    break;
                                case PayloadTypeEnum.DEV_AUDIO:
                                    _devAudioRxQueue.Add(data);
                                    break;
                            }
                        }
                        else
                        {
                            _logger.LogDebug($"client found that this datagram is repeated,ignored.dataId={payload.DatagramId},type={payload.DataType},checksum={payload.DataCheckSum},datalen={payload.DataLen}");
                        }
                    }
                    else
                    {
                        _logger.LogDebug("payload session id mismatch");
                    }
                }
                else
                {
                    _logger.LogDebug("received data's tag was client sent but im the client,so who sent it?");
                }
            }
            else
            {
                _logger.LogDebug("received data was invalid");
            }
        }
        private void EnqueueLinkControlPayload(byte[] data, LinkControlCmdEnum cmd, Guid sessionId, IPEndPoint remoteEP, LcpReqInfo? lcpReqInfo = null, ParametersWithIV? cryptoKey = null)
        {
            if (lcpReqInfo != null)
            {
                _lcpReqInfos.Add(lcpReqInfo);
            }
            LinkControlPayload lcp = new LinkControlPayload(false, cmd, data);
            byte[] lcpBytes = lcp.WriteToBytes();
            if (cryptoKey != null)
            {
                lcpBytes = MyProtocolCryptoHelper.SM4Encrypt(lcpBytes, cryptoKey);
            }
            _waitToSendQueue.Add(new MyWaitToSendDataInfo(sessionId, true, false, cryptoKey != null, false, PayloadTypeEnum.LINK_CONTROL, lcpBytes, remoteEP));
        }
        private async void ReceiveDataAsync()
        {
            _recvRunning = true;
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        var result = await _clientSock.ReceiveFromAsync(_buf, _remoteEndPoint, _cts.Token);
                        ProcessOnReceived(_buf, result.ReceivedBytes, (IPEndPoint)result.RemoteEndPoint);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpClientSide ReceiveDataMethod in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (SocketException se)
                    {
                        _logger.LogError(se, $"UdpClientSide ReceiveDataMethod in-loop received SocketException:{se.Message},exit");
                        Exit();
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpClientSide ReceiveDataMethod in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpClientSide ReceiveDataMethod Excpetion:{ex.Message}");
            }
            _recvRunning = false;
        }
        private void SendDevCtrlDataLoop()
        {
            _sendCtrlRunning = true;
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        byte[] data = _devCtrlTxQueue.Take();
                        if (_authStageInfo != null && _authStageInfo.AuthStage == MyProtocolAuthStageEnum.AUTH_COMPLETE && _sessionInfo != null)
                        {
                            byte[] senddata = data;
                            if (_sessionInfo.SessionKey != null)
                            {
                                senddata = MyProtocolCryptoHelper.SM4Encrypt(data, _sessionInfo.SessionKey);
                            }
                            _waitToSendQueue.Add(new MyWaitToSendDataInfo(_sessionInfo.SessionId, true, false, _sessionInfo.SessionKey != null, false, PayloadTypeEnum.DEV_CONTROL, senddata, _remoteEndPoint));
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpClientSide SendDevCtrlDataAsync in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpClientSide SendDevCtrlDataAsync in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpClientSide SendDevCtrlDataAsync Excpetion:{ex.Message}");
            }
            _sendCtrlRunning = false;
        }
        private void SendDevAudioDataLoop()
        {
            _sendAudioRunning = true;
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        byte[] data = _devAudioTxQueue.Take();
                        if (_authStageInfo != null && _authStageInfo.AuthStage == MyProtocolAuthStageEnum.AUTH_COMPLETE && _sessionInfo != null)
                        {
                            byte[] senddata = data;
                            if (_sessionInfo.SessionKey != null)
                            {
                                senddata = MyProtocolCryptoHelper.SM4Encrypt(data, _sessionInfo.SessionKey);
                            }
                            _waitToSendQueue.Add(new MyWaitToSendDataInfo(_sessionInfo.SessionId, true, false, _sessionInfo.SessionKey != null, false, PayloadTypeEnum.DEV_AUDIO, senddata, _remoteEndPoint));
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpClientSide SendDevAudioDataAsync in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpClientSide SendDevAudioDataAsync in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpClientSide SendDevAudioDataAsync Excpetion:{ex.Message}");
            }
            _sendAudioRunning = false;
        }
        private async Task TakeQueueToSendAsync()
        {
            _takeQueueToSendRunning = true;
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        MyWaitToSendDataInfo info = _waitToSendQueue.Take();
                        int dataId = 1;
                        bool changeId = false;
                        int repeatTimes = _eachPayloadRepeatTimes;
                        if (info.SessionId == Guid.Empty)
                        {
                            if (_authStageInfo != null)
                            {
                                dataId = _authStageInfo.LastSendDatagramId;
                                changeId = dataId == 0;
                                _authStageInfo.LastSendDatagramId = dataId + 1;
                                if (_authStageInfo.LastSendDatagramId == int.MaxValue)
                                    _authStageInfo.LastSendDatagramId = 0;
                            }
                        }
                        else
                        {
                            if (_sessionInfo != null)
                            {
                                dataId = _sessionInfo.LastSendDatagramId;
                                changeId = dataId == 0;
                                _sessionInfo.LastSendDatagramId = dataId + 1;
                                if (_sessionInfo.LastSendDatagramId == int.MaxValue)
                                    _sessionInfo.LastSendDatagramId = 0;
                                repeatTimes = _sessionInfo.RepeatTimes;
                            }
                        }
                        MyProtocolPayload pl = new MyProtocolPayload(info.SessionId, dataId, info.IsClientSent, info.IsResponse, info.IsEncrypted, changeId, info.IsHistoryReply, info.PayloadType, info.Data);
                        byte[] bytes = pl.WriteToBytes();
                        _ = Task.Run(async () =>
                        {
                            try
                            {
                                for (int i = 0; i < (info.PayloadType != PayloadTypeEnum.LINK_CONTROL ? repeatTimes : 1); i++)
                                {
                                    await _clientSock.SendToAsync(bytes, info.RemoteEndPoint, _cts.Token);
                                }
                            }
                            catch (SocketException se)
                            {
                                _logger.LogError(se, "发送时出现socket异常");
                                if (_sessionInfo != null && _sessionInfo.SessionId == info.SessionId)
                                {
                                    _logger.LogInformation("由于发送失败，将此会话移除");
                                    _sessionInfo = null;
                                    _authStageInfo = null;
                                    SessionDroped?.Invoke(this, EventArgs.Empty);
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.LogError(ex, "发送时出现异常");
                            }
                        });
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpClientSide TakeQueueToSendAsync in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpClientSide TakeQueueToSendAsync in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpClientSide TakeQueueToSendAsync Excpetion:{ex.Message}");
            }
            _takeQueueToSendRunning = false;
        }

        public void Dispose()
        {
            Exit();
        }
    }
}
